//
//  PJMUI.swift
//  PJMProFramework
//
//  Created by hui wang on 2024/11/24.
//

import Foundation
import UIKit


public extension UITableView {
    func registerCell(_ cls: AnyClass) {
        let clsName: String = "\(cls)"
        if Bundle.main.path(forResource: clsName, ofType: "nib") != nil {
            self.register(UINib(nibName: clsName, bundle: nil), forCellReuseIdentifier: clsName)
        } else {
            self.register(cls, forCellReuseIdentifier: clsName)
        }
    }
    
    func dequeueReusableCell <T> (_ cls: T.Type, _ indexPath: IndexPath) -> T {
        guard let cell = self.dequeueReusableCell(withIdentifier: "\(cls)", for: indexPath) as? T else {
            fatalError("'ex.registerCell' is must be used.")
        }
        return cell
    }
}


public extension UIColor {
    convenience init(hex: String) {
        var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
        hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")

        var rgb: UInt64 = 0

        var r: CGFloat = 0.0
        var g: CGFloat = 0.0
        var b: CGFloat = 0.0
        var a: CGFloat = 1.0

        let length = hexSanitized.count

        guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else {
            self.init(red: 0, green: 0, blue: 0, alpha: 1)
            return
        }

        if length == 6 {
            r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
            g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
            b = CGFloat(rgb & 0x0000FF) / 255.0

        } else if length == 8 {
            r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
            g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
            b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
            a = CGFloat(rgb & 0x000000FF) / 255.0
        } else {
            self.init(red: 0, green: 0, blue: 0, alpha: 1)
            return
        }

        self.init(red: r, green: g, blue: b, alpha: a)
    }
}

private var eventBlockKey: UInt8  = 0
extension UIControl {
    func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping()->()) {
        @objc class ClosureSleeve: NSObject {
            let closure:()->()
            init(_ closure: @escaping()->()) { self.closure = closure }
            @objc func invoke() { closure() }
        }
        let sleeve = ClosureSleeve(closure)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
        objc_setAssociatedObject(self, "\(UUID())", sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
}
public protocol UIControlActionProtocol: UIControl {
 
}

public extension UIControlActionProtocol {
  
    @discardableResult
    func tap(_ block: @escaping (Self) -> Void) -> Self {
         return event(.touchUpInside, block: block)
     }

     @discardableResult
     func event(_ event: UIControl.Event = .touchUpInside, block: @escaping (Self) -> ()) -> Self {
         if #available(iOS 14.0, *) {
             addAction(UIAction(handler: { action in
                 if let sender = action.sender as? Self {
                     block(sender)
                 } else {
                     fatalError("System Error")
                 }
             }), for: event)
         } else {
             self.addAction(for: .touchUpInside) { [unowned self] in
                 block(self)
             }
         }
         return self
     }
    
}

public extension UIView {
    
    @discardableResult
    func tapGesture(_ block: @escaping (Self) -> Void) -> Self {
        self.addTapGesture { [unowned self] in
            block(self)
        }
        return self
    }
    
    @discardableResult
    func addTapGesture(_ closure: @escaping () -> ()) -> UITapGestureRecognizer {
        @objc class ClosureSleeve: NSObject {
            let closure: () -> ()
            init(_ closure: @escaping () -> ()) { self.closure = closure }
            @objc func invoke() { closure() }
        }
        self.isUserInteractionEnabled = true
        let sleeve = ClosureSleeve(closure)
        let tapGesture = UITapGestureRecognizer(target: sleeve, action: #selector(ClosureSleeve.invoke))
        self.addGestureRecognizer(tapGesture)
        objc_setAssociatedObject(self, "\(UUID())", sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        return tapGesture
    }
}

public extension UIImage {
    func compress(toKb size: Int) -> (image: UIImage, data: Data) {
        let num: Int = 1000
        let maxLength: Int = size * num
        var compression: CGFloat = 1
        guard var data = self.jpegData(compressionQuality: compression) ?? self.pngData(),
              data.count > maxLength else { return (self, self.jpegData(compressionQuality: 1.0) ?? self.pngData() ?? Data()) }
        debugPrint("<====")
        debugPrint("Before compressing quality, image size =", data.count / num, "KB")
        
        // Compress by size
        var max: CGFloat = 1
        var min: CGFloat = 0
        for _ in 0..<6 {
            compression = (max + min) / 2
            data = self.jpegData(compressionQuality: compression)!
            debugPrint("[compress]: Compression =", compression)
            debugPrint("[compress]: In compressing quality loop, image size =", data.count / num, "KB")
            if CGFloat(data.count) < CGFloat(maxLength) * 0.9 {
                min = compression
            } else if data.count > maxLength {
                max = compression
            } else {
                break
            }
        }
        debugPrint("[compress]: After compressing quality, image size =", data.count / num, "KB")
        var resultImage: UIImage = UIImage(data: data)!
        if data.count < maxLength { return (resultImage, data) }
        
        // Compress by size
        var lastDataLength: Int = 0
        while data.count > maxLength, data.count != lastDataLength {
            lastDataLength = data.count
            let ratio: CGFloat = CGFloat(maxLength) / CGFloat(data.count)
            debugPrint("[compress]: Ratio =", ratio)
            let size: CGSize = CGSize(width: Int(resultImage.size.width * sqrt(ratio)),
                                      height: Int(resultImage.size.height * sqrt(ratio)))
            UIGraphicsBeginImageContext(size)
            resultImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
            resultImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            data = resultImage.jpegData(compressionQuality: compression)!
            debugPrint("[compress]: In compressing size loop, image size =", data.count / num, "KB")
        }
        debugPrint("[compress]: After compressing size loop, image size =", data.count / num, "KB")
        debugPrint("====>")
        return (resultImage, data)
    }
}
